package behaviour;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;

public class Explorer implements Strategy {
	
	private Coordinate currentCoordinate;
	private ArrayList<Direction> path = new ArrayList<Direction>();
	private Maze maze;
	private Coordinate coordinatePacman;
	private boolean pacmanFound;
	public void setCoordinatePacman(Coordinate pacman){
		coordinatePacman=pacman;
		pacmanFound=true;
	}
	public Explorer (Maze maze){
		this.maze=maze;
	}
	@Override
	public Direction nextDirection(Location loc, Maze[] mazes, Coordinate[] ghosts) {
		Direction ret;
		
		//maze.draw(maze.getCoordinateOfLocation(loc).getX(),maze.getCoordinateOfLocation(loc).getY());
		
		if(true/*path.size() == 0*/)
		{
			currentCoordinate=maze.getCoordinateOfLocation(loc);
			//System.out.println(currentCoordinate);
			//Coordinate[] closestUnkown = maze.pathToClosestUnknown(currentCoordinate.getX(), currentCoordinate.getY(), true);
			Coordinate[] closestUnkown = pathToClosestUnknown(currentCoordinate.getX(), currentCoordinate.getY(),mazes);
			if(closestUnkown == null)//maze volledig verkend
			{
				return null;
			}
//			for(int i=0; i<closestUnkown.length; i++)
//			{
//				path.add(currentCoordinate.getDirectionOfCoordinate(closestUnkown[i]));
//				currentCoordinate = closestUnkown[i];
//			}
			path.add(currentCoordinate.getDirectionOfCoordinate(closestUnkown[0]));
			currentCoordinate = closestUnkown[0];
		}
		
		ret = path.get(0);
		path.remove(0);
		
		return ret;
	}
	@Override
	public boolean exploreEverything() {
		// TODO Auto-generated method stub
		return false;
	}
	//assumes the current sector is explored
	public Coordinate[] pathToClosestUnknown(int x, int y, boolean ownCoordinates) {
		class RecursiveCoordinate {
			Coordinate c;
			RecursiveCoordinate rc;
			RecursiveCoordinate(Coordinate c, RecursiveCoordinate rc) {
				this.c=c;
				this.rc=rc;
			}
			@Override
			public boolean equals(Object other) {
				try {
					return ((RecursiveCoordinate)other).c.equals(this.c);
				} catch(Exception e) {
					return false;
				}
			}
			@Override
			public int hashCode() {
				return this.c.hashCode();
			}
		}

		Vector<RecursiveCoordinate> passed = new Vector<RecursiveCoordinate>();
		Vector<RecursiveCoordinate> active = new Vector<RecursiveCoordinate>();
		active.add(new RecursiveCoordinate(new Coordinate(x,y),null)); //start from our current location
		RecursiveCoordinate target = null;

		while(active.size()>0) { //as long as there might be longer paths
			Vector<RecursiveCoordinate> temp = new Vector<RecursiveCoordinate>();
			for(RecursiveCoordinate rc : active) {
				passed.add(rc);
				//NORTH
				if(rc.c.y<=maze.getMaxY()) { //check whether we're not going out of bounds
					int val = maze.read(rc.c.x,rc.c.y,Direction.NORTH,true); //get the wall we're looking at
					//create a new RecursiveCoordinate with the coordinate we're adding, referring to our current element of active
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x,rc.c.y+1), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman))
							val=1;
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					} else if(val==0) { //but if we have an unexplored wall
						target = rc; //then that's where we want to go
						temp.clear(); //and we don't want the while-loop to continue
						break; //neither do we want the for-loop to continue
					}
				}
				//EAST
				if(rc.c.x<=maze.getMaxX()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.EAST,true);
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x+1,rc.c.y), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman))
						val=1;
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(val==0) {
						target = rc;
						temp.clear();
						break;
					}
				}
				//SOUTH
				if(rc.c.y>=maze.getMinY()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.SOUTH,true);
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x,rc.c.y-1), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman))
						val=1;
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(val==0) {
						target = rc;
						temp.clear();
						break;
					}
				}
				//WEST
				if(rc.c.x>=maze.getMinX()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.WEST,true);
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x-1,rc.c.y), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman))
						val=1;
					if(val==2 && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(val==0) {
						target = rc;
						temp.clear();
						break;
					}
				}

			}
			active = temp; //prepare for the next iteration of the while loop
		}
		if(target==null || target.rc==null) return null;//the maze is explored || the current sector is not explored
		Vector<Coordinate> path = new Vector<Coordinate>();
		while(target.rc!=null) { //as long as we don't reach our starting point
			path.add(0,target.c); //prepend the coordinate from the target to the path
			target = target.rc; //go to the coordinate before it in the path
		}
		//path.removeElementAt(0); //we don't want the starting position included


		return path.toArray(new Coordinate[1]);
	}

	//assumes the current sector is explored
	public Coordinate[] pathToClosestUnknown(int x, int y, Maze[] mazes) {
		if(mazes.length==0) return pathToClosestUnknown(x, y, true);
		class RecursiveCoordinate {
			Coordinate c;
			RecursiveCoordinate rc;
			RecursiveCoordinate(Coordinate c, RecursiveCoordinate rc) {
				this.c=c;
				this.rc=rc;
			}
			@Override
			public boolean equals(Object other) {
				try {
					return ((RecursiveCoordinate)other).c.equals(this.c);
				} catch(Exception e) {
					return false;
				}
			}
			@Override
			public int hashCode() {
				return this.c.hashCode();
			}
		}
		//actual method starts here
		Vector<RecursiveCoordinate> passed = new Vector<RecursiveCoordinate>();
		Vector<RecursiveCoordinate> active = new Vector<RecursiveCoordinate>();
		active.add(new RecursiveCoordinate(new Coordinate(x,y),null)); //start from our current location
		RecursiveCoordinate target = null;

		while(active.size()>0) { //as long as there might be longer paths
			Vector<RecursiveCoordinate> temp = new Vector<RecursiveCoordinate>();
			for(RecursiveCoordinate rc : active) {
				passed.add(rc);
				//NORTH
				if(rc.c.y<=maze.getMaxY()) { //check whether we're not going out of bounds
					int val = maze.read(rc.c.x,rc.c.y,Direction.NORTH,true); //get the wall we're looking at
					boolean consensus = true;
					if(mazes[0] == null)
						System.out.println("mazes[0] is null");
					int otherValue = mazes[0].read(rc.c.x,rc.c.y,Direction.NORTH,false);
					//mazes[0].draw(x,y);
					for(Maze m : mazes) {
						int tempValue = m.read(rc.c.x,rc.c.y,Direction.NORTH,false); //get the opinion of the other mazes
						if(otherValue == 0 && tempValue != 0)
							otherValue = tempValue;
						else if(otherValue != 0 && tempValue == 0)
							tempValue = otherValue;
						consensus = consensus && tempValue==otherValue;
					}
					//create a new RecursiveCoordinate with the coordinate we're adding, referring to our current element of active
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x,rc.c.y+1), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman)) {
						val=1;
						otherValue = 1;
						consensus = true;
					}
					boolean noWall = val==2 || (consensus && otherValue==2);
					boolean unexploredWall = val==0 && (otherValue==0 || (!consensus));
					if(noWall && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) { //if there's no wall, and the new coordinate hasn't been checked yet
						temp.add(t); //add it to the list of coordinates to use as active in the next iteration of the while-loop
					} else if(unexploredWall) { //but if we have an unexplored wall
						target = rc; //then that's where we want to go
						temp.clear(); //and we don't want the while-loop to continue
						break; //neither do we want the for-loop to continue
					}
				}
				//EAST
				if(rc.c.x<=maze.getMaxX()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.EAST,true);
					boolean consensus = true;
					int otherValue = mazes[0].read(rc.c.x,rc.c.y,Direction.EAST,false);
					for(Maze m : mazes) {
						int tempValue = m.read(rc.c.x,rc.c.y,Direction.EAST,false); //get the opinion of the other mazes
						if(otherValue == 0 && tempValue != 0)
							otherValue = tempValue;
						else if(otherValue != 0 && tempValue == 0)
							tempValue = otherValue;
						consensus = consensus && tempValue==otherValue;
					}
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x+1,rc.c.y), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman)) {
						val=1;
						otherValue = 1;
						consensus = true;
					}
					boolean noWall = val==2 || (consensus && otherValue==2);
					boolean unexploredWall = val==0 && (otherValue==0 || (!consensus));
					if(noWall && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(unexploredWall) {
						target = rc;
						temp.clear();
						break;
					}
				}
				//SOUTH
				if(rc.c.y>=maze.getMinY()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.SOUTH,true);
					boolean consensus = true;
					int otherValue = mazes[0].read(rc.c.x,rc.c.y,Direction.SOUTH,false);
					for(Maze m : mazes) {
						int tempValue = m.read(rc.c.x,rc.c.y,Direction.SOUTH,false); //get the opinion of the other mazes
						if(otherValue == 0 && tempValue != 0)
							otherValue = tempValue;
						else if(otherValue != 0 && tempValue == 0)
							tempValue = otherValue;
						consensus = consensus && tempValue==otherValue;
					}
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x,rc.c.y-1), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman)) {
						val=1;
						otherValue = 1;
						consensus = true;
					}
					boolean noWall = val==2 || (consensus && otherValue==2);
					boolean unexploredWall = val==0 && (otherValue==0 || (!consensus));
					if(noWall && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(unexploredWall) {
						target = rc;
						temp.clear();
						break;
					}
				}
				//WEST
				if(rc.c.x>=maze.getMinX()) {
					int val = maze.read(rc.c.x,rc.c.y,Direction.WEST,true);
					boolean consensus = true;
					int otherValue = mazes[0].read(rc.c.x,rc.c.y,Direction.WEST,false);
					for(Maze m : mazes) {
						int tempValue = m.read(rc.c.x,rc.c.y,Direction.WEST,false); //get the opinion of the other mazes
						if(otherValue == 0 && tempValue != 0)
							otherValue = tempValue;
						else if(otherValue != 0 && tempValue == 0)
							tempValue = otherValue;
						consensus = consensus && tempValue==otherValue;
					}
					RecursiveCoordinate t = new RecursiveCoordinate(new Coordinate(rc.c.x-1,rc.c.y), rc);
					if(pacmanFound&&t.c.equals(coordinatePacman)) {
						val=1;
						otherValue = 1;
						consensus = true;
					}
					boolean noWall = val==2 || (consensus && otherValue==2);
					boolean unexploredWall = val==0 && (otherValue==0 || (!consensus));
					if(noWall && active.indexOf(t)==-1 && temp.indexOf(t)==-1 && passed.indexOf(t)==-1) {
						temp.add(t);
					} else if(unexploredWall) {
						target = rc;
						temp.clear();
						break;
					}
				}

			}
			active = temp; //prepare for the next iteration of the while loop
		}
		if(target==null || target.rc==null) return null;//the maze is explored || the current sector is not explored
		Vector<Coordinate> path = new Vector<Coordinate>();
		while(target.rc!=null) { //as long as we don't reach our starting point
			path.add(0,target.c); //prepend the coordinate from the target to the path
			target = target.rc; //go to the coordinate before it in the path
		}
		//path.removeElementAt(0); //we don't want the starting position included

		return path.toArray(new Coordinate[1]);
	}

}
